﻿namespace Blaze.Framework.LuaExtensions
{
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Animations;
    using XLua;

    /// <summary>
    /// 用于传递Unity的相关事件到Lua脚本。
    /// </summary>
    public class LuaStateMachineBehaviour : StateMachineBehaviour
    {
        [HideInInspector]
        public List<LuaFieldPair> Fields = new List<LuaFieldPair>();

        public string LuaFilePath = string.Empty;

        /// <summary>
        /// 获取关联的<see cref="LuaTable"/>。
        /// </summary>
        public LuaTable LuaTable { get; private set; }

        /// <summary>
        /// 获取当前<see cref="LuaBehaviour"/>所使用的Lua模块。
        /// </summary>
        protected virtual ILuaModule LuaModule
        {
            get { return Bootstrap.Instance.LuaModule; }
        }

        /// <summary>
        /// 根据设置好的<see cref="LuaFilePath"/>加载Lua脚本。
        /// </summary>
        /// <returns>是否加载成功</returns>
        public bool Initialize()
        {
            if (mIsInitialized)
                return true;
            if (LuaTable == null)
            {
                var module = LuaModule.Require(LuaFilePath);
                if (module == null)
                {
                    Debug.LogError("Lua table not found -> " + LuaFilePath);
                    return false;
                }

                var constructor = module.Get<LuaFunction>("new");
                if (constructor == null)
                {
                    Debug.LogError(LuaFilePath + ".new() function required");
                    return false;
                }

                LuaTable = constructor.Func<LuaTable>();

                if (LuaTable == null)
                {
                    Debug.LogError(LuaFilePath + " Invalid new() function");
                    return false;
                }
            }

            LuaTable.Set("behaviour", this);

            mFunctionCache.Action("Awake");
            mFunctionCache.Action("OnEnable");

            LuaModule.Reloading += onReloadingLua;
            mIsInitialized = true;
            return true;
        }

#if UNITY_EDITOR
        /// <summary>
        /// 在lua中实现实现OnInspectorGUI来自己画编辑器界面。
        /// </summary>
        public void OnInspectorGUI()
        {
            //credit to heroboy
            if (!Application.isPlaying || !mIsInitialized)
                return;

            try
            {
                mFunctionCache.Action("OnInspectorGUI");
            }
            catch (LuaException e)
            {
                UnityEditor.EditorGUILayout.HelpBox(e.Message, UnityEditor.MessageType.Error);
            }
        }
#endif

        private void onReloadingLua()
        {
            LuaModule.Reloading -= onReloadingLua;
            if (!mIsInitialized)
                return;

            //TODO:release

            if (LuaTable != null)
                LuaTable.Dispose();

            mIsInitialized = false;
        }

        private readonly Dictionary<string, LuaFieldPair> mFields = new Dictionary<string, LuaFieldPair>();

        private bool mIsInitialized;

        private readonly FunctionCache mFunctionCache = new FunctionCache();

        #region Unity built-in methods

        protected void Awake()
        {
            Initialize();
        }

        protected void OnDisable()
        {
            mFunctionCache.Action("OnDisable");
        }

        protected void OnEnable()
        {
            mFunctionCache.Action("OnEnable");
        }

        #endregion

        #region StateMachineBehaviour methods

        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            mFunctionCache.Action("OnStateEnter", animator, stateInfo, layerIndex, default(AnimatorControllerPlayable));
        }

        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            mFunctionCache.Action("OnStateEnter", animator, stateInfo, layerIndex, controller);
        }

        public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            mFunctionCache.Action("OnStateExit", animator, stateInfo, layerIndex, default(AnimatorControllerPlayable));
        }

        public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            mFunctionCache.Action("OnStateExit", animator, stateInfo, layerIndex, controller);
        }

        public override void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            mFunctionCache.Action("OnStateIK", animator, stateInfo, layerIndex, default(AnimatorControllerPlayable));
        }

        public override void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            mFunctionCache.Action("OnStateIK", stateInfo, layerIndex, controller);
        }

        /// <summary>
        /// <para>
        /// Called on the first Update frame when making a transition to a StateMachine. This is not called when making a transition into a StateMachine sub-state.
        /// </para>
        /// </summary>
        /// <param name="animator">The Animator playing this state machine.</param><param name="stateMachinePathHash">The full path hash for this state machine.</param>
        public override void OnStateMachineEnter(Animator animator, int stateMachinePathHash)
        {
            mFunctionCache.Action("OnStateMachineEnter", animator, stateMachinePathHash, default(AnimatorControllerPlayable));
        }

        public override void OnStateMachineEnter(Animator animator, int stateMachinePathHash, AnimatorControllerPlayable controller)
        {
            mFunctionCache.Action("OnStateMachineEnter", animator, stateMachinePathHash, controller);
        }

        /// <summary>
        /// <para>
        /// Called on the last Update frame when making a transition out of a StateMachine. This is not called when making a transition into a StateMachine sub-state.
        /// </para>
        /// </summary>
        /// <param name="animator">The Animator playing this state machine.</param><param name="stateMachinePathHash">The full path hash for this state machine.</param>
        public override void OnStateMachineExit(Animator animator, int stateMachinePathHash)
        {
            mFunctionCache.Action("OnStateMachineExit", animator, stateMachinePathHash, default(AnimatorControllerPlayable));
        }

        public override void OnStateMachineExit(Animator animator, int stateMachinePathHash, AnimatorControllerPlayable controller)
        {
            mFunctionCache.Action("OnStateMachineExit", animator, stateMachinePathHash, controller);
        }

        public override void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            mFunctionCache.Action("OnStateMove", animator, stateInfo, layerIndex, default(AnimatorControllerPlayable));
        }

        public override void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            mFunctionCache.Action("OnStateMove", animator, stateInfo, layerIndex, controller);
        }

        public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            mFunctionCache.Action("OnStateUpdate", animator, stateInfo, layerIndex, default(AnimatorControllerPlayable));
        }

        public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            mFunctionCache.Action("OnStateUpdate", animator, stateInfo, layerIndex, controller);
        }

        #endregion
    }
}